import hashlib
import logging
from datetime import datetime
from typing import Any, List

from fastapi import APIRouter, Depends, Body, HTTPException, Path, UploadFile, File, Form
from fastapi.params import Param
from sqlalchemy import or_
from sqlalchemy.orm import Session, Query

from app.core.security import get_current_user
from app.db.session import get_db
from app.llama_index.session.livsm_session import livsmSessions, MilvusVectorStoreSession
from app.models import User, KnowledgeBase, Document
from app.models.knowledge import  KnowledgeBaseStatus, DocumentStatus
from app.schemas.base_so import BaseListQo, DelParam, DocumentListQo, ChangeStatusParam, ChangeStatusToDraftParam, \
    LookupSksTableKnowledgeUseAgentQO
from app.schemas.knowledge_base import KnowledgeBaseCreate, KnowledgeBaseUpdate, DocumentUpdate
from app.schemas.result import BaseResult, okResult, errResult, BaseListResult, BaseSksTableResult, sksTableResult
from app.services.factory.embedding_factory import EmbeddingsFactory
from app.services.file_services import asave_kb_file, FilePathInfo, delete_kb_file, format_file_name_to_read
from app.services.knowledge_services import knowledge_services_change_knowledge_doc_status
from app.utils.str_utils import format_filename
from app.utils.time_utils import now_time

logger = logging.getLogger(__name__)
router = APIRouter()

@router.get("/test", response_model=BaseResult)
def test(
) -> Any:
    """
    测试接口
    http://127.0.0.1:8880/api/knowledge/test
    """
    logger.info(f"test")
    return okResult(data='asus')

@router.post("/list", response_model=BaseListResult)
def get_knowledge_list(
        db: Session = Depends(get_db),
        current_user: User = Depends(get_current_user),
        qo:BaseListQo=Body()
) -> Any:
    page=qo.pageNum
    per_page = qo.pageSize
    offset = (page - 1) * per_page

    query:Query=(db.query(KnowledgeBase)
        # .filter(KnowledgeBase.user_id == current_user.id)
         )

    total=query.count()

    knowledge_bases = (
        query
        .order_by(KnowledgeBase.id)
        .limit(per_page)
        .offset(offset)
        .all()
    )
    return BaseListResult(
        rows=knowledge_bases,
        total=total,
    ).json_encoders()


@router.get("/getDetail/{kb_id}", response_model=BaseResult)
def get_knowledge_base(
        *,
        db: Session = Depends(get_db),
        kb_id: int=Path(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    Get knowledge base by ID.
    """
    from sqlalchemy.orm import joinedload

    kb = (
        db.query(KnowledgeBase)
        .filter(
            KnowledgeBase.id == kb_id,
        )
        .first()
    )

    if not kb:
        raise HTTPException(status_code=500, detail="找不到知识库")

    return okResult(data=kb).json_encoders()


@router.post("/add", response_model=BaseResult)
def create_knowledge_base(
        *,
        db: Session = Depends(get_db),
        kb_in: KnowledgeBaseCreate=Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    Create new knowledge base.
    """
    kb = KnowledgeBase(
        name=kb_in.name,
        description=kb_in.description,
        user_id=current_user.id,
        dimensions=kb_in.dimensions,
        enable_sparse=kb_in.enable_sparse,
        status=KnowledgeBaseStatus.DRAFT.value.value,
    )
    db.add(kb)
    db.commit()
    # 刷新会话以获取新生成的主键
    db.flush()
    db.refresh(kb)
    logger.info(f"Knowledge base created: {kb.name} for user {current_user.id}")
    return kb
@router.post("/initMilvus", response_model=BaseResult)
async def init_milvus(
        db: Session = Depends(get_db),
        kb_id: int=Param(),
        current_user: User = Depends(get_current_user)
) -> Any:
    kb:KnowledgeBase = db.query(KnowledgeBase).filter(
        KnowledgeBase.id == kb_id,
        ).first()
    if kb is None:
        return errResult(data='知识库不存在或已被删除')

    MilvusVectorStoreSession.drop_collection(kb_id)
    livsmSessions.remove_session(kb.id)
    livsmSessions.get_session(
        kb_id=kb.id,
        dimensions=kb.dimensions,
        enable_sparse=kb.enable_sparse
    )
    kb.status=KnowledgeBaseStatus.FINISH.value.value
    db.add(kb)
    db.commit()
    # 刷新会话以获取新生成的主键
    db.flush()
    db.refresh(kb)
    return okResult().json_encoders()

@router.put("/edit", response_model=BaseResult)
def update_knowledge_base(
        *,
        db: Session = Depends(get_db),
        kb_in: KnowledgeBaseUpdate=Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    Update knowledge base.
    """
    kb = db.query(KnowledgeBase).filter(
        KnowledgeBase.id == kb_in.id,
    ).first()

    if not kb:
        raise HTTPException(status_code=500, detail="知识库不存在或已被删除")

    for field, value in kb_in.dict(exclude_unset=True).items():
        setattr(kb, field, value)

    db.add(kb)
    db.commit()
    db.refresh(kb)
    logger.info(f"Knowledge base updated: {kb.name} for user {current_user.id}")
    return okResult(data=kb).json_encoders()


@router.delete("/delete")
async def delete_knowledge_base(
        *,
        db: Session = Depends(get_db),
        param:DelParam=Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    Delete knowledge base and all associated resources.
    """
    ids=param.ids

    query= (   db.query(KnowledgeBase)
        .filter(
            KnowledgeBase.id.in_(ids),
        )
    )
    query.delete()
    db.commit()
    return okResult()


@router.post("/documents/upload")
async def upload_kb_documents(
        kb_id: int=Form(...,description='知识库ID'),
        file_description:str=Form(...,description='文件信息'),
        files: List[UploadFile]=File(...,description='上传的文件'),
        db: Session = Depends(get_db),
        current_user: User = Depends(get_current_user)
):
    """
    Upload multiple documents to MinIO.
    """
    kb = db.query(KnowledgeBase).filter(
        KnowledgeBase.id == kb_id,
        KnowledgeBase.user_id == current_user.id
    ).first()
    if not kb:
        raise HTTPException(status_code=404, detail="Knowledge base not found")

    if len(files) > 1:
        return errResult(msg='一次只能上传一个文件')
    if len(files) == 0:
        return errResult(msg='未选择要上传的文件')

    for file in files:
        # 1. 计算文件 hash
        file_content = await file.read()
        file_hash = hashlib.sha256(file_content).hexdigest()

        # 3. 上传到临时目录
        file_path_info:FilePathInfo = await asave_kb_file(kb_id=kb_id, file=file,file_desc=file.filename,file_content=file_content)

        # 4. 创建上传记录
        upload = Document(
            description=file_description,
            knowledge_base_id=kb_id,
            file_name=format_filename(file.filename),
            file_hash=file_hash,
            file_size=file.size,
            content_type=file.content_type,
            sub_path=file_path_info.sub_path,
            status=DocumentStatus.DRAFT.value.value
        )
        db.add(upload)
        db.commit()
        db.refresh(upload)


    return okResult(upload).json_encoders()


@router.post("/doc/list", response_model=BaseListResult)
def get_knowledge_doc_list(
        db: Session = Depends(get_db),
        current_user: User = Depends(get_current_user),
        qo:DocumentListQo=Body()
) -> Any:
    page=qo.pageNum
    per_page = qo.pageSize
    offset = (page - 1) * per_page

    conditions = [Document.knowledge_base_id == qo.kb_id]
    if qo.file_name:
        conditions.append(or_(Document.file_name.like(f"%{qo.file_name}%"),Document.description.like(f"%{qo.file_name}%")))
    if qo.status:
        conditions.append(Document.status == qo.status)

    query:Query=(db.query(Document)
                 .filter(*conditions)
                 )

    total=query.count()

    mainList = (
        query
        .order_by(Document.id)
        .limit(per_page)
        .offset(offset)
        .all()
    )
    return BaseListResult(
        rows=mainList,
        total=total,
    ).json_encoders()

@router.get("/doc/getDetail/{doc_id}", response_model=BaseResult)
def get_document(
        *,
        db: Session = Depends(get_db),
        doc_id: int=Path(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    Get knowledge base by ID.
    """
    from sqlalchemy.orm import joinedload

    doc = (
        db.query(Document)
        .filter(
            Document.id == doc_id,
            )
        .first()
    )

    if not doc:
        raise HTTPException(status_code=500, detail="找不到文件")

    return okResult(data=doc).json_encoders()

@router.put("/doc/edit", response_model=BaseResult)
def update_knowledge_doc(
        *,
        db: Session = Depends(get_db),
        doc_in: DocumentUpdate=Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    Update knowledge base.
    """
    doc = db.query(Document).filter(
        Document.id == doc_in.id,
        ).first()

    if not doc:
        raise HTTPException(status_code=500, detail="知识库不存在或已被删除")


    for field, value in doc_in.dict(exclude_unset=True).items():
        setattr(doc, field, value)
        logger.info(f"doc setattr: {field} = {value}")

    db.add(doc)
    db.commit()
    db.refresh(doc)
    logger.info(f"document updated: {doc.description} for user {current_user.id}")
    return okResult(data=doc).json_encoders()


@router.post("/doc/processing")
async def processing_knowledge_doc(
        *,
        db: Session = Depends(get_db),
        param:DelParam=Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    向量化
    """
    ids=param.ids

    query:List[Document]= (   db.query(Document)
               .filter(
        Document.id.in_(ids),
    ).all()
               )
    # errorMsg=[]
    # successMsg=[]

    errorList=[]
    successList=[]
    try:
        for doc in query:
            kb:KnowledgeBase=db.query(KnowledgeBase).filter(KnowledgeBase.id == doc.knowledge_base_id).first()
            doc.error_message=''
            if doc.status not in [DocumentStatus.DRAFT.value.value]:
                logger.error(f'ID= {doc.id} 的文件 {doc.description}状态为 {doc.status}[{DocumentStatus.getLabel(doc.status)}] ，不能进行向量化')
                # errorMsg.append(f'ID= {doc.id} 的文件 {doc.description}状态为 {doc.status}[{DocumentStatus.getLabel(doc.status)}] ，不能删除')
                doc.error_message=f'状态为 {doc.status}[{DocumentStatus.getLabel(doc.status)}] ，不能进行向量化'
                errorList.append(doc.to_dict_use_result())
                # 更新错误信息
                db.add(doc)
                db.commit()
                continue
            try:
                mvss:MilvusVectorStoreSession=livsmSessions.get_session(
                    kb_id=kb.id,
                    dimensions=kb.dimensions,
                    enable_sparse=kb.enable_sparse
                )
                doc.status = DocumentStatus.PROCESSING.value.value
                doc.status_time = now_time()
                db.add(doc)
                db.commit()
                db.refresh(doc)
                mvss.addDoc(doc=doc)
            except Exception as e:
                logger.error(f'ID= {doc.id} 的文件 {doc.description},进行向量化 {doc.sub_path} 时失败:{str(e)}', exc_info=True)
                # logger.error(f'ID= {doc.id} 的文件 {doc.description},进行向量化 {doc.sub_path} 时失败:{str(e)}',e)
                doc.error_message=f'进行向量化 {doc.sub_path} 时失败'
                errorList.append(doc.to_dict_use_result())
                db.add(doc)
                db.commit()
                continue
            successList.append(doc.to_dict_use_result())
            doc.status=DocumentStatus.CLOSED.value.value
            doc.status_time=now_time()
            db.add(doc)
            db.commit()
            # successMsg.append(f'ID= {doc.id} 的文件 {doc.description} 删除成功')
        # result={'errorMsg':errorMsg,'successMsg':successMsg,'errorList':errorList,'successList':successList}
        result={'errorList':errorList,'successList':successList}
        resultJson = okResult(data=result).json_encoders()
        return resultJson
    except Exception as e:
        db.rollback()
        logger.error(f"Failed to processing_knowledge_doc {param.ids}: {str(e)}",e)
        raise HTTPException(status_code=500, detail=f"向量化失败: {str(e)}")

@router.post("/doc/changeStatusToDraft")
async def change_knowledge_doc_status_to_draft(
        *,
        db: Session = Depends(get_db),
        param:ChangeStatusToDraftParam =Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    状态全部改为草稿
    """
    ids=param.ids

    query:List[Document]= (   db.query(Document)
                              .filter(
        Document.id.in_(ids),
    ).all()
                              )

    errorList=[]
    successList=[]
    try:
        for doc in query:
            kb: KnowledgeBase = db.query(KnowledgeBase).filter(KnowledgeBase.id == doc.knowledge_base_id).first()
            doc.error_message=''
            try:
                mvss:MilvusVectorStoreSession=livsmSessions.get_session(
                    kb_id=kb.id,
                    dimensions=kb.dimensions,
                    enable_sparse=kb.enable_sparse
                )
                #删除向量化记录
                mvss.delDoc(doc=doc)
                doc.status = DocumentStatus.DRAFT.value.value
                doc.status_time = now_time()
                successList.append(doc.to_dict_use_result())
                db.add(doc)
                db.commit()
                db.refresh(doc)
            except Exception as e:
                logger.error(f'ID= {doc.id} 的文件 {doc.description},进行向量化 {doc.sub_path} 时失败:{str(e)}')
                doc.error_message=f'变更状态为草稿时时失败'
                errorList.append(doc.to_dict_use_result())
                db.add(doc)
                db.commit()
                db.refresh(doc)
        result={'errorList':errorList,'successList':successList}
        resultJson = okResult(data=result).json_encoders()
        return resultJson
    except Exception as e:
        db.rollback()
        logger.error(f"Failed to change_knowledge_doc_status_to_draft {param.ids}: {str(e)}",e)
        raise HTTPException(status_code=500, detail=f"变更状态为草稿时失败: {str(e)}")

@router.post("/doc/changeStatus")
async def change_knowledge_doc_status(
        *,
        db: Session = Depends(get_db),
        param:ChangeStatusParam =Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    变更状态
    """
    doc= (   db.query(Document)
    .filter(
        Document.id == param.id
    ).first()
               )

    try:
        kb: KnowledgeBase = db.query(KnowledgeBase).filter(KnowledgeBase.id == doc.knowledge_base_id).first()
        hasError=knowledge_services_change_knowledge_doc_status(kb=kb,doc=doc,status=param.status)
        logger.info(f'ID= {doc.id} 的文件 {doc.description} 变更状态为 {doc.status}[{DocumentStatus.getLabel(doc.status)}]')
        logger.info(f'ID= {doc.id} 的文件 {doc.description} ,{doc.error_message}')
        # 更新错误信息
        db.add(doc)
        db.commit()
        db.flush(doc)
        if hasError:
            return okResult()
        return errResult(msg=doc.error_message)
    except Exception as e:
        db.rollback()
        logger.error(f"Failed to change document status {param.id}: {str(e)}",e)
        raise HTTPException(status_code=500, detail=f"变更文件状态失败: {str(e)}")
@router.delete("/doc/delete")
async def delete_knowledge_doc(
        *,
        db: Session = Depends(get_db),
        param:DelParam=Body(),
        current_user: User = Depends(get_current_user)
) -> Any:
    """
    删除文件
    """
    ids=param.ids

    query= (   db.query(Document)
           .filter(
            Document.id.in_(ids),
            ).all()
    )
    # errorMsg=[]
    # successMsg=[]

    errorList=[]
    successList=[]
    try:
        for doc in query:
            doc.error_message=''
            if doc.status not in [DocumentStatus.DRAFT.value.value]:
                # errorMsg.append(f'ID= {doc.id} 的文件 {doc.description}状态为 {doc.status}[{DocumentStatus.getLabel(doc.status)}] ，不能删除')
                doc.error_message=f'状态为 {doc.status}[{DocumentStatus.getLabel(doc.status)}] ，不能删除'
                errorList.append(doc.to_dict_use_result())
                # 更新错误信息
                db.add(doc)
                db.commit()
                continue
            try:
                await delete_kb_file(doc.sub_path)
            except Exception as e:
                logger.error(f'ID= {doc.id} 的文件 {doc.description},删除持久化文件 {doc.sub_path} 时失败:{str(e)}')
                # errorMsg.append(f'ID= {doc.id} 的文件 {doc.description},删除持久化文件 {doc.sub_path} 时失败')
                doc.error_message = f'删除持久化文件 {doc.sub_path} 时失败'
                errorList.append(doc.to_dict_use_result())
            successList.append(doc.to_dict_use_result())
            db.delete(doc)
            db.commit()
            # successMsg.append(f'ID= {doc.id} 的文件 {doc.description} 删除成功')
        # result={'errorMsg':errorMsg,'successMsg':successMsg,'errorList':errorList,'successList':successList}
        result={'errorList':errorList,'successList':successList}
        resultJson = okResult(data=result).json_encoders()
        return resultJson
    except Exception as e:
        db.rollback()
        logger.error(f"Failed to delete document {param.ids}: {str(e)}",e)
        raise HTTPException(status_code=500, detail=f"删除文件失败: {str(e)}")


@router.get("/list1", response_model=BaseResult)
def get_knowledge_list1(
) -> Any:
    """
    Retrieve knowledge bases.
    """
    logger.info(f"list")
    text='hello'
    try:
        res=EmbeddingsFactory.create().get_embedding(text)
        return okResult(data=res)
    except Exception as e:
        logger.error('get_knowledge_list error', exc_info=True)
        return errResult(data=str(e.__repr__),msg=str(e))

@router.post("/lookup-sks-table-use-agent", response_model=BaseSksTableResult)
def lookup_sks_table_knowledge_use_agent(
        db: Session = Depends(get_db),
        current_user: User = Depends(get_current_user),
        qo: LookupSksTableKnowledgeUseAgentQO = Body()
) -> Any:
    conditions = []
    conditions.append(KnowledgeBase.status == KnowledgeBaseStatus.FINISH.dictValue)

    # if qo.searchValue:
    #     conditions.append(
    #         or_(AIAgent.system_prompt.like(f"%{qo.searchValue}%"), AIAgent.name.like(f"%{qo.searchValue}%"),
    #             AIAgent.description.like(f"%{qo.searchValue}%")))

    query: Query = (db.query(KnowledgeBase)
                    .filter(*conditions)
                    )


    rows = (
        query
        .order_by(KnowledgeBase.id.desc())
        .all()
    )
    total= len(rows)
    return sksTableResult(
        list=rows,
        total=total,
    ).json_encoders()

